home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / STUTTGART / TEMP / GNU / flex / Performanc < prev    next >
Text File  |  1995-06-28  |  9KB  |  346 lines

  1. Performance
  2. Previous: <Options=>Options> * Next: <C++=>C> * Up: <Top=>!Root>
  3.  
  4. #Wrap on
  5. {fH3}Performance considerations{f}
  6.  
  7. The main design goal of {fCode}flex{f} is that it generate
  8. high-performance scanners.  It has been optimized for dealing
  9. well with large sets of rules.  Aside from the effects on
  10. scanner speed of the table compression {fEmphasis}-C{f} options outlined
  11. above, there are a number of options\/actions which degrade
  12. performance.  These are, from most expensive to least:
  13.  
  14. #Wrap off
  15. #fCode
  16. REJECT
  17. %option yylineno
  18. arbitrary trailing context
  19.  
  20. pattern sets that require backing up
  21. %array
  22. %option interactive
  23. %option always-interactive
  24.  
  25. '^' beginning-of-line operator
  26. yymore()
  27. #f
  28. #Wrap on
  29.  
  30. with the first three all being quite expensive and the
  31. last two being quite cheap.  Note also that {fEmphasis}unput(){f} is
  32. implemented as a routine call that potentially does quite
  33. a bit of work, while {fEmphasis}yyless(){f} is a quite-cheap macro; so
  34. if just putting back some excess text you scanned, use
  35. {fEmphasis}yyless(){f}.
  36.  
  37. {fCode}REJECT{f} should be avoided at all costs when performance is
  38. important.  It is a particularly expensive option.
  39.  
  40. Getting rid of backing up is messy and often may be an
  41. enormous amount of work for a complicated scanner.  In
  42. principal, one begins by using the {fEmphasis}-b{f} flag to generate a
  43. {fCite}lex.backup{f} file.  For example, on the input
  44.  
  45. #Wrap off
  46. #fCode
  47. %%
  48. foo        return TOK\_KEYWORD;
  49. foobar     return TOK\_KEYWORD;
  50. #f
  51. #Wrap on
  52.  
  53. the file looks like:
  54.  
  55. #Wrap off
  56. #fCode
  57. State \#6 is non-accepting -
  58.  associated rule line numbers:
  59.        2       3
  60.  out-transitions: [ o ]
  61.  jam-transitions: EOF [ \\001-n  p-\\177 ]
  62.  
  63. State \#8 is non-accepting -
  64.  associated rule line numbers:
  65.        3
  66.  out-transitions: [ a ]
  67.  jam-transitions: EOF [ \\001-`  b-\\177 ]
  68.  
  69. State \#9 is non-accepting -
  70.  associated rule line numbers:
  71.        3
  72.  out-transitions: [ r ]
  73.  jam-transitions: EOF [ \\001-q  s-\\177 ]
  74.  
  75. Compressed tables always back up.
  76. #f
  77. #Wrap on
  78.  
  79. The first few lines tell us that there's a scanner state
  80. in which it can make a transition on an 'o' but not on any
  81. other character, and that in that state the currently
  82. scanned text does not match any rule.  The state occurs
  83. when trying to match the rules found at lines 2 and 3 in
  84. the input file.  If the scanner is in that state and then
  85. reads something other than an 'o', it will have to back up
  86. to find a rule which is matched.  With a bit of
  87. head-scratching one can see that this must be the state it's in
  88. when it has seen "fo".  When this has happened, if
  89. anything other than another 'o' is seen, the scanner will
  90. have to back up to simply match the 'f' (by the default
  91. rule).
  92.  
  93. The comment regarding State \#8 indicates there's a problem
  94. when "foob" has been scanned.  Indeed, on any character
  95. other than an 'a', the scanner will have to back up to
  96. accept "foo".  Similarly, the comment for State \#9
  97. concerns when "fooba" has been scanned and an 'r' does not
  98. follow.
  99.  
  100. The final comment reminds us that there's no point going
  101. to all the trouble of removing backing up from the rules
  102. unless we're using {fEmphasis}-Cf{f} or {fEmphasis}-CF{f}, since there's no
  103. performance gain doing so with compressed scanners.
  104.  
  105. The way to remove the backing up is to add "error" rules:
  106.  
  107. #Wrap off
  108. #fCode
  109. %%
  110. foo         return TOK\_KEYWORD;
  111. foobar      return TOK\_KEYWORD;
  112.  
  113. fooba       |
  114. foob        |
  115. fo          \{
  116.             \/\* false alarm, not really a keyword \*\/
  117.             return TOK\_ID;
  118.             \}
  119. #f
  120. #Wrap on
  121.  
  122. Eliminating backing up among a list of keywords can also
  123. be done using a "catch-all" rule:
  124.  
  125. #Wrap off
  126. #fCode
  127. %%
  128. foo         return TOK\_KEYWORD;
  129. foobar      return TOK\_KEYWORD;
  130.  
  131. [a-z]+      return TOK\_ID;
  132. #f
  133. #Wrap on
  134.  
  135. This is usually the best solution when appropriate.
  136.  
  137. Backing up messages tend to cascade.  With a complicated
  138. set of rules it's not uncommon to get hundreds of
  139. messages.  If one can decipher them, though, it often only
  140. takes a dozen or so rules to eliminate the backing up
  141. (though it's easy to make a mistake and have an error rule
  142. accidentally match a valid token.  A possible future {fCode}flex{f}
  143. feature will be to automatically add rules to eliminate
  144. backing up).
  145.  
  146. It's important to keep in mind that you gain the benefits
  147. of eliminating backing up only if you eliminate {fEmphasis}every{f}
  148. instance of backing up.  Leaving just one means you gain
  149. nothing.
  150.  
  151. {fStrong}Variable{f} trailing context (where both the leading and
  152. trailing parts do not have a fixed length) entails almost
  153. the same performance loss as {fCode}REJECT{f} (i.e., substantial).
  154. So when possible a rule like:
  155.  
  156. #Wrap off
  157. #fCode
  158. %%
  159. mouse|rat\/(cat|dog)   run();
  160. #f
  161. #Wrap on
  162.  
  163. is better written:
  164.  
  165. #Wrap off
  166. #fCode
  167. %%
  168. mouse\/cat|dog         run();
  169. rat\/cat|dog           run();
  170. #f
  171. #Wrap on
  172.  
  173. or as
  174.  
  175. #Wrap off
  176. #fCode
  177. %%
  178. mouse|rat\/cat         run();
  179. mouse|rat\/dog         run();
  180. #f
  181. #Wrap on
  182.  
  183. Note that here the special '|' action does {fEmphasis}not{f} provide any
  184. savings, and can even make things worse (see Deficiencies
  185. \/ Bugs below).
  186.  
  187. Another area where the user can increase a scanner's
  188. performance (and one that's easier to implement) arises from
  189. the fact that the longer the tokens matched, the faster
  190. the scanner will run.  This is because with long tokens
  191. the processing of most input characters takes place in the
  192. (short) inner scanning loop, and does not often have to go
  193. through the additional work of setting up the scanning
  194. environment (e.g., {fCode}yytext{f}) for the action.  Recall the
  195. scanner for C comments:
  196.  
  197. #Wrap off
  198. #fCode
  199. %x comment
  200. %%
  201.         int line\_num = 1;
  202.  
  203. "\/\*"         BEGIN(comment);
  204.  
  205. <comment>[^\*\\n]\*
  206. <comment>"\*"+[^\*\/\\n]\*
  207. <comment>\\n             ++line\_num;
  208. <comment>"\*"+"\/"        BEGIN(INITIAL);
  209. #f
  210. #Wrap on
  211.  
  212. This could be sped up by writing it as:
  213.  
  214. #Wrap off
  215. #fCode
  216. %x comment
  217. %%
  218.         int line\_num = 1;
  219.  
  220. "\/\*"         BEGIN(comment);
  221.  
  222. <comment>[^\*\\n]\*
  223. <comment>[^\*\\n]\*\\n      ++line\_num;
  224. <comment>"\*"+[^\*\/\\n]\*
  225. <comment>"\*"+[^\*\/\\n]\*\\n ++line\_num;
  226. <comment>"\*"+"\/"        BEGIN(INITIAL);
  227. #f
  228. #Wrap on
  229.  
  230. Now instead of each newline requiring the processing of
  231. another action, recognizing the newlines is "distributed"
  232. over the other rules to keep the matched text as long as
  233. possible.  Note that {fEmphasis}adding{f} rules does {fEmphasis}not{f} slow down the
  234. scanner!  The speed of the scanner is independent of the
  235. number of rules or (modulo the considerations given at the
  236. beginning of this section) how complicated the rules are
  237. with regard to operators such as '\*' and '|'.
  238.  
  239. A final example in speeding up a scanner: suppose you want
  240. to scan through a file containing identifiers and
  241. keywords, one per line and with no other extraneous
  242. characters, and recognize all the keywords.  A natural first
  243. approach is:
  244.  
  245. #Wrap off
  246. #fCode
  247. %%
  248. asm      |
  249. auto     |
  250. break    |
  251. … etc …
  252. volatile |
  253. while    \/\* it's a keyword \*\/
  254.  
  255. .|\\n     \/\* it's not a keyword \*\/
  256. #f
  257. #Wrap on
  258.  
  259. To eliminate the back-tracking, introduce a catch-all
  260. rule:
  261.  
  262. #Wrap off
  263. #fCode
  264. %%
  265. asm      |
  266. auto     |
  267. break    |
  268. ... etc ...
  269. volatile |
  270. while    \/\* it's a keyword \*\/
  271.  
  272. [a-z]+   |
  273. .|\\n     \/\* it's not a keyword \*\/
  274. #f
  275. #Wrap on
  276.  
  277. Now, if it's guaranteed that there's exactly one word per
  278. line, then we can reduce the total number of matches by a
  279. half by merging in the recognition of newlines with that
  280. of the other tokens:
  281.  
  282. #Wrap off
  283. #fCode
  284. %%
  285. asm\\n    |
  286. auto\\n   |
  287. break\\n  |
  288. … etc …
  289. volatile\\n |
  290. while\\n  \/\* it's a keyword \*\/
  291.  
  292. [a-z]+\\n |
  293. .|\\n     \/\* it's not a keyword \*\/
  294. #f
  295. #Wrap on
  296.  
  297. One has to be careful here, as we have now reintroduced
  298. backing up into the scanner.  In particular, while {fEmphasis}we{f} know
  299. that there will never be any characters in the input
  300. stream other than letters or newlines, {fCode}flex{f} can't figure
  301. this out, and it will plan for possibly needing to back up
  302. when it has scanned a token like "auto" and then the next
  303. character is something other than a newline or a letter.
  304. Previously it would then just match the "auto" rule and be
  305. done, but now it has no "auto" rule, only a "auto\\n" rule.
  306. To eliminate the possibility of backing up, we could
  307. either duplicate all rules but without final newlines, or,
  308. since we never expect to encounter such an input and
  309. therefore don't how it's classified, we can introduce one
  310. more catch-all rule, this one which doesn't include a
  311. newline:
  312.  
  313. #Wrap off
  314. #fCode
  315. %%
  316. asm\\n    |
  317. auto\\n   |
  318. break\\n  |
  319. … etc …
  320. volatile\\n |
  321. while\\n  \/\* it's a keyword \*\/
  322.  
  323. [a-z]+\\n |
  324. [a-z]+   |
  325. .|\\n     \/\* it's not a keyword \*\/
  326. #f
  327. #Wrap on
  328.  
  329. Compiled with {fEmphasis}-Cf{f}, this is about as fast as one can get a
  330. {fCode}flex{f} scanner to go for this particular problem.
  331.  
  332. A final note: {fCode}flex{f} is slow when matching NUL's,
  333. particularly when a token contains multiple NUL's.  It's best to
  334. write rules which match {fEmphasis}short{f} amounts of text if it's
  335. anticipated that the text will often include NUL's.
  336.  
  337. Another final note regarding performance: as mentioned
  338. above in the section How the Input is Matched, dynamically
  339. resizing {fCode}yytext{f} to accommodate huge tokens is a slow
  340. process because it presently requires that the (huge) token
  341. be rescanned from the beginning.  Thus if performance is
  342. vital, you should attempt to match "large" quantities of
  343. text but not "huge" quantities, where the cutoff between
  344. the two is at about 8K characters\/token.
  345.  
  346.